package com.danbay.framework.shiro.config;

import com.danbay.framework.shiro.ShrioRedisCacheManager;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.AnonymousFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.web.filter.DelegatingFilterProxy;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author lenovo
 */
@ConditionalOnProperty("shiro")
@Slf4j
@ConfigurationProperties
public class ShiroConfigAdapter {

	/**
	 * 使用缓存
	 */
	@Value("${shiro.cachingEnabled:true}")
	private boolean cachingEnabled;

	@Value("${shiro.authenticationCachingEnabled:true}")
	private boolean authenticationCachingEnabled;

	@Value("${shiro.authorizationCachingEnabled:true}")
	private boolean authorizationCachingEnabled;

	@Autowired
	private RedisConnectionFactory redisConnectionFactory;

	@Autowired
	private AuthorizingRealm realm;

	/**
	 * FilterRegistrationBean
	 * 
	 * @return
	 */
	@Bean
	public FilterRegistrationBean filterRegistrationBean() {
		FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
		filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
		filterRegistration.setEnabled(true);
		filterRegistration.addUrlPatterns("/*");
		filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
		return filterRegistration;
	}

	/**
	 * 需要重写的方法，并加上@Primary注解，增加filters和filter chain设置
	 * @see org.apache.shiro.spring.web.ShiroFilterFactoryBean
	 * @return
	 */
	@Bean(name = "shiroFilter")
	public ShiroFilterFactoryBean shiroFilter() {
		ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
		bean.setSecurityManager(securityManager());
		bean.setLoginUrl("/login");
		bean.setUnauthorizedUrl("/unauthor");

		Map<String, Filter> filters = new HashMap<>();
		filters.put("anon", new AnonymousFilter());
		bean.setFilters(filters);

		Map<String, String> chains = new HashMap<>();
		chains.put("/**", "authc,perms");
		bean.setFilterChainDefinitionMap(chains);
		return bean;
	}

	/**
	 * 权限管理器
	 * 
	 * @return
	 */
	@Bean(name = "securityManager")
	public DefaultWebSecurityManager securityManager() {
		DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
		//设置认证缓存
		realm.setCacheManager(redisCacheManager());
		realm.setCachingEnabled(cachingEnabled);
		realm.setAuthenticationCachingEnabled(authenticationCachingEnabled);
		realm.setAuthorizationCachingEnabled(authorizationCachingEnabled);
		// 身份认证实现
		manager.setRealm(realm);
		// session 管理器
		manager.setSessionManager(sessionManager());
		// 缓存管理器
		manager.setCacheManager(redisCacheManager());
		return manager;
	}

	/**
	 * DefaultWebSessionManager
	 * 
	 * @return
	 */
	@Bean(name = "sessionManager")
	public ServletContainerSessionManager sessionManager() {
		ServletContainerSessionManager sessionManager = new ServletContainerSessionManager();
		return sessionManager;
	}

	/**
	 * 使用redis作为缓存
	 * @return
	 */
	@Bean(name = "shrioRedisCacheManager")
	public ShrioRedisCacheManager redisCacheManager() {
		ShrioRedisCacheManager cacheManager = new ShrioRedisCacheManager();
		cacheManager.createCache("shiro_redis:");
		return cacheManager;
	}

	@Bean
	public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
		return new LifecycleBeanPostProcessor();
	}

}